#include <RSLib/Common.hpp>
#ifndef RSLIB___WINDOWS_HPP
#define RSLIB___WINDOWS_HPP
 
 
 
#if defined(RSLIB___WINDOWS) 
 
 
 
#if !defined(RSLIB___WINDOWS_MIN_VERSION) && !defined(_WIN32_WINNT)
	#define RSLIB___WINDOWS_MIN_VERSION 0x0501
	#define _WIN32_WINNT RSLIB___WINDOWS_MIN_VERSION
#elif defined(RSLIB___WINDOWS_MIN_VERSION) && !defined(_WIN32_WINNT)
	#define _WIN32_WINNT RSLIB___WINDOWS_MIN_VERSION
#elif !defined(RSLIB___WINDOWS_MIN_VERSION) && defined(_WIN32_WINNT)
	#define RSLIB___WINDOWS_MIN_VERSION _WIN32_WINNT
#else
	#if RSLIB___WINDOWS_MIN_VERSION != _WIN32_WINNT
		#error RSLIB___WINDOWS_MIN_VERSION != _WIN32_WINNT
	#endif
#endif
#include <windows.h>
#include <windowsx.h>
#pragma comment(lib,"winmm.lib")
 
 
//---FUNCTIONS
//	Desc: handle microsoft abuse of macro namespace...
//-------------------------------------------------->
#pragma push_macro("MoveMemory")
#undef MoveMemory
inline VOID MoveMemory(VOID UNALIGNED *Destination, CONST VOID UNALIGNED *Source, SIZE_T Length)
{
#pragma pop_macro("MoveMemory")
	MoveMemory(Destination,Source,Length);
#undef MoveMemory
}
#pragma push_macro("CopyMemory")
#undef CopyMemory
inline VOID CopyMemory(VOID UNALIGNED *Destination, CONST VOID UNALIGNED *Source, SIZE_T Length)
{
#pragma pop_macro("CopyMemory")
	CopyMemory(Destination,Source,Length);
#undef CopyMemory
}
#pragma push_macro("ZeroMemory")
#undef ZeroMemory
inline VOID ZeroMemory(VOID UNALIGNED *Destination, SIZE_T Length)
{
#pragma pop_macro("ZeroMemory")
	ZeroMemory(Destination,Length);
#undef ZeroMemory
}
 
 
//---INCLUDE
#include <RSLib/Types.hpp>
#include <RSLib/String.hpp>
 
 
 


 
 
namespace RSLib{
namespace Windows{
 
class Exception
{
public:
	DWORD error;
	const Char8* file;
	Unsigned32 line;
	const Char8* function;
	const Char8* message;
	
public:
	Exception(const Char8* message, Dword error, const Char8* file, Unsigned32 line, const Char8* function)
	{
		this->error = error;
		this->file = file;
		this->line = line;
		this->function = function;
		this->message = message;
	}
	
	//operator const Char8*()
	//{
	//	return String8()
	//}
};
#define THROW_WIN32() throw RSLib::Windows::Exception(NULL, GetLastError(),__FILE__,__LINE__,__FUNCSIG__)
#define THROW_WIN32_MESSAGE(message) throw RSLib::Windows::Exception(message, GetLastError(),__FILE__,__LINE__,__FUNCSIG__)
 
}//namespace Windows
}//namespace RSLib
 
 
 
//---FUNCTION
//	Desc: dynamically loaded GetModuleHandleEx
//			used for Windows2000 compatibility
//----------------------------------------------->
#if RSLIB___WINDOWS_MIN_VERSION < 0x0501
#define GET_MODULE_HANDLE_EX_FLAG_PIN                 (0x00000001)
#define GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT  (0x00000002)
#define GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS        (0x00000004)
 
inline BOOL GetModuleHandleEx(DWORD dwFlags, LPCTSTR lpModuleName, HMODULE* phModule)
{
	HMODULE kernel32_dll = GetModuleHandle("Kernel32.dll");
	
	BOOL(WINAPI *getModuleHandleEx)(DWORD,LPCSTR,HMODULE*) = (BOOL(WINAPI *)(DWORD,LPCSTR,HMODULE*)) GetProcAddress(kernel32_dll,"GetModuleHandleExA");
	if (getModuleHandleEx == NULL) return FALSE;
	
	return getModuleHandleEx(dwFlags,lpModuleName,phModule);
}
#endif
 
 
 
namespace RSLib{
namespace Windows{
 
 
 
//---FUNCTION
//------------------------------------------------------------------------------------------->
inline HRESULT ModifyPrivilege(IN LPCTSTR szPrivilege, IN DWORD newAttrib, OUT DWORD* pOldAttrib=NULL)
{
	HRESULT	hr	= S_OK;
	TOKEN_PRIVILEGES	NewState, OldState;
	LUID	luid;
	HANDLE hToken	= NULL;

	// Open	the process	token	for this	process.
	if (!OpenProcessToken(GetCurrentProcess(),
								TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
								&hToken ))
	{			 
		return	ERROR_FUNCTION_FAILED;
	}

	// Get the	local	unique id for the	privilege.
	if (	!LookupPrivilegeValue( NULL,
										szPrivilege,
										&luid ))
	{
		CloseHandle(	hToken );		
		return	ERROR_FUNCTION_FAILED;
	}

	// Assign values	to	the TOKEN_PRIVILEGE structure.
	NewState.PrivilegeCount =	1;
	NewState.Privileges[0].Luid = luid;
	NewState.Privileges[0].Attributes = newAttrib;
	
	OldState.PrivilegeCount =	1;
	OldState.Privileges[0].Luid = luid;

	DWORD size	= sizeof(OldState);

	// Adjust the	token	privilege.
	if (!AdjustTokenPrivileges(hToken,
										FALSE,
										&NewState,
										sizeof(NewState),
										&OldState,
										&size))
	{			 
		hr = ERROR_FUNCTION_FAILED;
	}
	
	if (pOldAttrib != NULL) *pOldAttrib =	OldState.Privileges[0].Attributes;	

	// Close the handle.
	CloseHandle(hToken);

	return hr;
}
inline HINSTANCE GetInstanceFromAddress(Pointer address)
{
	HMODULE module;
	return GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT|GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,(LPCSTR)address, &module) == TRUE ? module : 0;
} 
 
 
 
inline String8 ErrorString(DWORD errorCode)
{
	LPVOID lpMsgBuf;
	if (!FormatMessage( 	FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
								NULL,errorCode,MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),(LPTSTR) &lpMsgBuf,0,NULL )) return String8("error");
	
	String8 message( (LPCTSTR)lpMsgBuf );
	
	LocalFree( lpMsgBuf );
	
	return message;
}
 
 
 
//---FUNCTION
//	Name:	GetThreadWindow
//	Desc:	retrieve the handle for the i-th window of the thread
//------------------------------------------------------------>
inline BOOL CALLBACK EnumThreadWndProc(HWND hwnd,LPARAM lParam)
{
	struct ENUMED_WINDOW
	{
		UINT32 current;
		UINT32 index;
		HWND handle;
	}* p = (ENUMED_WINDOW*)lParam;
	
	p->current++;										//increase counter
	if (p->current != p->index) return TRUE;	//if not desired index continue enumeration
	p->handle = hwnd;									//report handle
	return FALSE;										//stop enumeration
}
inline HWND GetThreadWindow(DWORD threadId = GetCurrentThreadId(), UINT32 i=0)
{
	struct ENUMED_WINDOW
	{
		UINT32 current;
		UINT32 index;
		HWND handle;
	}w;
	w.current = -1;
	w.index = i;
	w.handle = NULL;
	
	BOOL bRes = EnumThreadWindows( threadId, EnumThreadWndProc,(LPARAM)&w);
	
	
	return w.handle;
}
 
 
 
//---FUNCTION
//----------------------------->
#if (RSLIB___WINDOWS_MIN_VERSION >= 0x0500)
inline Boolean SetWindowOpacity(HWND hWnd, Float32 opacity)
{
//	update window style
	SetWindowLong(hWnd, GWL_EXSTYLE, GetWindowLong(hWnd, GWL_EXSTYLE) | WS_EX_LAYERED);
 	
//	set opacity
 	BYTE alpha = BYTE(opacity * 255);
 	return SetLayeredWindowAttributes(hWnd, NULL, alpha, LWA_ALPHA) == TRUE;
}
#else
typedef BOOL (WINAPI *SetLayeredWindowAttributesProc)(HWND, COLORREF, BYTE, DWORD);
#define WS_EX_LAYERED           0x00080000
#define LWA_COLORKEY            0x00000001
#define LWA_ALPHA               0x00000002
inline Boolean SetWindowOpacity(HWND hWnd, Float32 opacity)
{
//	runtime link procedure
	BOOL(WINAPI *setLayeredWindowAttributes)(HWND,COLORREF,BYTE,DWORD) = 
		(BOOL(WINAPI*)(HWND,COLORREF,BYTE,DWORD)) GetProcAddress(GetModuleHandle("user32.dll"),"SetLayeredWindowAttributes");
	if (setLayeredWindowAttributes == NULL) return false;
	
//	update window style
	SetWindowLong(hWnd, GWL_EXSTYLE, GetWindowLong(hWnd, GWL_EXSTYLE) | WS_EX_LAYERED);
 	
//	set opacity
 	return setLayeredWindowAttributes(hWnd, NULL, BYTE(opacity * 255), LWA_ALPHA) == TRUE;
}
#endif
 
 
 
class CriticalSection
{
protected:
	CRITICAL_SECTION section;

public:
	CriticalSection()
	{
		InitializeCriticalSection(&section);
	}
	CriticalSection(IN Unsigned32 spinCount)
	{
#if RSLIB___WINDOWS_MIN_VERSION >= 0x0403 
		InitializeCriticalSectionAndSpinCount(&section, spinCount);
#else
		InitializeCriticalSection(&section);
#endif
	}
	~CriticalSection()
	{
		DeleteCriticalSection(&section);
	}
	
public:
	void Enter()	{EnterCriticalSection(&section);}
	bool Try()		{return TryEnterCriticalSection(&section) == TRUE;}
	void Leave()	{LeaveCriticalSection(&section);}
		
public:
	void SetSpinCount(IN Unsigned32 spinCount)
	{
	#if RSLIB___WINDOWS_MIN_VERSION >= 0x0403 
		SetCriticalSectionSpinCount(&section, spinCount);
	#endif
	}
	
public:
	class Lock
	{
	public:
		Lock(CriticalSection& mutex_) : mutex(mutex_)	{mutex.Enter();}
		~Lock()														{mutex.Leave();}
		
	private:
		CriticalSection& mutex;
	};
};
 
 
 
}//namespace Windows
}//namespace RSLib
 
 
  
#endif//defined(RSLIB___WINDOWS)
 
#endif//RSLIB___WINDOWS_HPP
